home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / shells / rc-1.000 / rc-1 / rc-1.5-linux / walk.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-07  |  8.2 KB  |  359 lines

  1. /* walk.c: walks the parse tree. */
  2.  
  3. #include <signal.h>
  4. #include <setjmp.h>
  5. #include "rc.h"
  6. #include "jbwrap.h"
  7.  
  8. /*
  9.    global which indicates whether rc is executing a test;
  10.    used by rc -e so that if (false) does not exit.
  11. */
  12. bool cond = FALSE;
  13.  
  14. static bool haspreredir(Node *);
  15. static bool isallpre(Node *);
  16. static bool dofork(bool);
  17. static void dopipe(Node *);
  18.  
  19. /* Tail-recursive version of walk() */
  20.  
  21. #define WALK(x, y) { n = x; parent = y; goto top; }
  22.  
  23. /* walk the parse-tree. "obvious". */
  24.  
  25. extern bool walk(Node *n, bool parent) {
  26. top:    sigchk();
  27.     if (n == NULL) {
  28.         if (!parent)
  29.             exit(0);
  30.         set(TRUE);
  31.         return TRUE;
  32.     }
  33.     switch (n->type) {
  34.     case nArgs: case nBackq: case nConcat: case nCount:
  35.     case nFlat: case nLappend: case nRedir: case nVar:
  36.     case nVarsub: case nWord: case nQword:
  37.         exec(glob(glom(n)), parent);    /* simple command */
  38.         break;
  39.     case nBody:
  40.         walk(n->u[0].p, TRUE);
  41.         WALK(n->u[1].p, parent);
  42.         /* WALK doesn't fall through */
  43.     case nNowait: {
  44.         int pid;
  45.         if ((pid = rc_fork()) == 0) {
  46. #if !defined(NOJOB) && defined(SIGTTOU) && defined(SIGTTIN) && defined(SIGTSTP)
  47.             setsigdefaults(FALSE);
  48.             rc_signal(SIGTTOU, SIG_IGN);    /* Berkeleyized version: put it in a new pgroup. */
  49.             rc_signal(SIGTTIN, SIG_IGN);
  50.             rc_signal(SIGTSTP, SIG_IGN);
  51.             setpgrp(0, getpid());
  52. #else
  53.             setsigdefaults(TRUE);        /* ignore SIGINT, SIGQUIT, SIGTERM */
  54. #endif
  55.             mvfd(rc_open("/dev/null", rFrom), 0);
  56.             walk(n->u[0].p, FALSE);
  57.             exit(getstatus());
  58.         }
  59.         if (interactive)
  60.             fprint(2, "%d\n", pid);
  61.         varassign("apid", word(nprint("%d", pid), NULL), FALSE);
  62.         redirq = NULL; /* kill pre-redir queue */
  63.         break;
  64.     }
  65.     case nAndalso: {
  66.         bool oldcond = cond;
  67.         cond = TRUE;
  68.         if (walk(n->u[0].p, TRUE)) {
  69.             cond = oldcond;
  70.             WALK(n->u[1].p, parent);
  71.         } else
  72.             cond = oldcond;
  73.         break;
  74.     }
  75.     case nOrelse: {
  76.         bool oldcond = cond;
  77.         cond = TRUE;
  78.         if (!walk(n->u[0].p, TRUE)) {
  79.             cond = oldcond;
  80.             WALK(n->u[1].p, parent);
  81.         } else
  82.             cond = oldcond;
  83.         break;
  84.     }
  85.     case nBang:
  86.         set(!walk(n->u[0].p, TRUE));
  87.         break;
  88.     case nIf: {
  89.         bool oldcond = cond;
  90.         Node *true_cmd = n->u[1].p, *false_cmd = NULL;
  91.         if (true_cmd != NULL && true_cmd->type == nElse) {
  92.             false_cmd = true_cmd->u[1].p;
  93.             true_cmd = true_cmd->u[0].p;
  94.         }
  95.         cond = TRUE;
  96.         if (!walk(n->u[0].p, TRUE))
  97.             true_cmd = false_cmd; /* run the else clause */
  98.         cond = oldcond;
  99.         WALK(true_cmd, parent);
  100.     }
  101.     case nWhile: {
  102.         Jbwrap j;
  103.         Edata jbreak;
  104.         Estack e1, e2;
  105.         bool testtrue, oldcond = cond;
  106.         cond = TRUE;
  107.         if (!walk(n->u[0].p, TRUE)) { /* prevent spurious breaks inside test */
  108.             cond = oldcond;
  109.             break;
  110.         }
  111.         if (setjmp(j.j))
  112.             break;
  113.         jbreak.jb = &j;
  114.         except(eBreak, jbreak, &e1);
  115.         do {
  116.             Edata block;
  117.             block.b = newblock();
  118.             cond = oldcond;
  119.             except(eArena, block, &e2);
  120.             walk(n->u[1].p, TRUE);
  121.             testtrue = walk(n->u[0].p, TRUE);
  122.             unexcept(); /* eArena */
  123.             cond = TRUE;
  124.         } while (testtrue);
  125.         cond = oldcond;
  126.         unexcept(); /* eBreak */
  127.         break;
  128.     }
  129.     case nForin: {
  130.         List *l, *var = glom(n->u[0].p);
  131.         Jbwrap j;
  132.         Estack e1, e2;
  133.         Edata jbreak;
  134.         if (setjmp(j.j))
  135.             break;
  136.         jbreak.jb = &j;
  137.         except(eBreak, jbreak, &e1);
  138.         for (l = listcpy(glob(glom(n->u[1].p)), nalloc); l != NULL; l = l->n) {
  139.             Edata block;
  140.             assign(var, word(l->w, NULL), FALSE);
  141.             block.b = newblock();
  142.             except(eArena, block, &e2);
  143.             walk(n->u[2].p, TRUE);
  144.             unexcept(); /* eArena */
  145.         }
  146.         unexcept(); /* eBreak */
  147.         break;
  148.     }
  149.     case nSubshell:
  150.         if (dofork(TRUE)) {
  151.             walk(n->u[0].p, FALSE);
  152.             rc_exit(getstatus());
  153.         }
  154.         break;
  155.     case nAssign:
  156.         if (n->u[0].p == NULL)
  157.             rc_error("null variable name");
  158.         assign(glom(n->u[0].p), glob(glom(n->u[1].p)), FALSE);
  159.         set(TRUE);
  160.         break;
  161.     case nPipe:
  162.         dopipe(n);
  163.         break;
  164.     case nNewfn: {
  165.         List *l = glom(n->u[0].p);
  166.         if (l == NULL)
  167.             rc_error("null function name");
  168.         while (l != NULL) {
  169.             if (dashex)
  170.                 prettyprint_fn(2, l->w, n->u[1].p);
  171.             fnassign(l->w, n->u[1].p);
  172.             l = l->n;
  173.         }
  174.         set(TRUE);
  175.         break;
  176.     }
  177.     case nRmfn: {
  178.         List *l = glom(n->u[0].p);
  179.         while (l != NULL) {
  180.             if (dashex)
  181.                 fprint(2, "fn %S\n", l->w);
  182.             fnrm(l->w);
  183.             l = l->n;
  184.         }
  185.         set(TRUE);
  186.         break;
  187.     }
  188.     case nDup:
  189.         redirq = NULL;
  190.         break; /* Null command */
  191.     case nMatch: {
  192.         List *a = glob(glom(n->u[0].p)), *b = glom(n->u[1].p);
  193.         if (dashex)
  194.             fprint(2, (a != NULL && a->n != NULL) ? "~ (%L) %L\n" : "~ %L %L\n", a, " ", b, " ");
  195.         set(lmatch(a, b));
  196.         break;
  197.     }
  198.     case nSwitch: {
  199.         List *v = glom(n->u[0].p);
  200.         while (1) {
  201.             do {
  202.                 n = n->u[1].p;
  203.                 if (n == NULL)
  204.                     return istrue();
  205.             } while (n->u[0].p == NULL || n->u[0].p->type != nCase);
  206.             if (lmatch(v, glom(n->u[0].p->u[0].p))) {
  207.                 for (n = n->u[1].p; n != NULL && (n->u[0].p == NULL || n->u[0].p->type != nCase); n = n->u[1].p)
  208.                     walk(n->u[0].p, TRUE);
  209.                 break;
  210.             }
  211.         }
  212.         break;
  213.     }
  214.     case nPre: {
  215.         List *v;
  216.         if (n->u[0].p->type == nRedir || n->u[0].p->type == nDup) {
  217.             if (redirq == NULL && !dofork(parent)) /* subshell on first preredir */
  218.                 break;
  219.             qredir(n->u[0].p);
  220.             if (!haspreredir(n->u[1].p))
  221.                 doredirs(); /* no more preredirs, empty queue */
  222.             walk(n->u[1].p, FALSE);
  223.             rc_exit(getstatus());
  224.             /* NOTREACHED */
  225.         } else if (n->u[0].p->type == nAssign) {
  226.             if (isallpre(n->u[1].p)) {
  227.                 walk(n->u[0].p, TRUE);
  228.                 WALK(n->u[1].p, parent);
  229.             } else {
  230.                 Estack e;
  231.                 Edata var;
  232.                 v = glom(n->u[0].p->u[0].p);
  233.                 assign(v, glob(glom(n->u[0].p->u[1].p)), TRUE);
  234.                 var.name = v->w;
  235.                 except(eVarstack, var, &e);
  236.                 walk(n->u[1].p, parent);
  237.                 varrm(v->w, TRUE);
  238.                 unexcept(); /* eVarstack */
  239.             }
  240.         } else
  241.             panic("unexpected node in preredir section of walk");
  242.         break;
  243.     }
  244.     case nBrace:
  245.         if (n->u[1].p == NULL) {
  246.             WALK(n->u[0].p, parent);
  247.         } else if (dofork(parent)) {
  248.             walk(n->u[1].p, TRUE); /* Do redirections */
  249.             redirq = NULL;   /* Reset redirection queue */
  250.             walk(n->u[0].p, FALSE); /* Do commands */
  251.             rc_exit(getstatus());
  252.             /* NOTREACHED */
  253.         }
  254.         break;
  255.     case nEpilog:
  256.         qredir(n->u[0].p);
  257.         if (n->u[1].p != NULL) {
  258.             WALK(n->u[1].p, parent); /* Do more redirections. */
  259.         } else {
  260.             doredirs();    /* Okay, we hit the bottom. */
  261.         }
  262.         break;
  263.     case nNmpipe:
  264.         rc_error("named pipes cannot be executed as commands");
  265.         /* NOTREACHED */
  266.     default:
  267.         panic("unknown node in walk");
  268.         /* NOTREACHED */
  269.     }
  270.     return istrue();
  271. }
  272.  
  273. /* checks to see whether there are any pre-redirections left in the tree */
  274.  
  275. static bool haspreredir(Node *n) {
  276.     while (n != NULL && n->type == nPre) {
  277.         if (n->u[0].p->type == nDup || n->u[0].p->type == nRedir)
  278.             return TRUE;
  279.         n = n->u[1].p;
  280.     }
  281.     return FALSE;
  282. }
  283.  
  284. /* checks to see whether a subtree is all pre-command directives, i.e., assignments and redirs only */
  285.  
  286. static bool isallpre(Node *n) {
  287.     while (n != NULL && n->type == nPre)
  288.         n = n->u[1].p;
  289.     return n == NULL || n->type == nRedir || n->type == nAssign || n->type == nDup;
  290. }
  291.  
  292. /*
  293.    A code-saver. Forks, child returns (for further processing in walk()), and the parent
  294.    waits for the child to finish, setting $status appropriately.
  295. */
  296.  
  297. static bool dofork(bool parent) {
  298.     int pid, sp;
  299.  
  300.     if (!parent || (pid = rc_fork()) == 0)
  301.         return TRUE;
  302.     redirq = NULL; /* clear out the pre-redirection queue in the parent */
  303.     rc_wait4(pid, &sp, TRUE);
  304.     setstatus(-1, sp);
  305.     sigchk();
  306.     return FALSE;
  307. }
  308.  
  309. static void dopipe(Node *n) {
  310.     int i, j, sp, pid, fd_prev, fd_out, pids[512], stats[512], p[2];
  311.     bool intr;
  312.     Node *r;
  313.  
  314.     fd_prev = fd_out = 1;
  315.     for (r = n, i = 0; r != NULL && r->type == nPipe; r = r->u[2].p, i++) {
  316.         if (i > 500) /* the only hard-wired limit in rc? */
  317.             rc_error("pipe too long");
  318.         if (pipe(p) < 0) {
  319.             uerror("pipe");
  320.             rc_error(NULL);
  321.         }
  322.         if ((pid = rc_fork()) == 0) {
  323.             redirq = NULL; /* clear preredir queue */
  324.             mvfd(p[0], r->u[1].i);
  325.             if (fd_prev != 1)
  326.                 mvfd(fd_prev, fd_out);
  327.             close(p[1]);
  328.             walk(r->u[3].p, FALSE);
  329.             exit(getstatus());
  330.         }
  331.         if (fd_prev != 1)
  332.             close(fd_prev); /* parent must close all pipe fd's */
  333.         pids[i] = pid;
  334.         fd_prev = p[1];
  335.         fd_out = r->u[0].i;
  336.         close(p[0]);
  337.     }
  338.     if ((pid = rc_fork()) == 0) {
  339.         mvfd(fd_prev, fd_out);
  340.         walk(r, FALSE);
  341.         exit(getstatus());
  342.         /* NOTREACHED */
  343.     }
  344.     redirq = NULL; /* clear preredir queue */
  345.     close(fd_prev);
  346.     pids[i++] = pid;
  347.  
  348.     /* collect statuses */
  349.  
  350.     intr = FALSE;
  351.     for (j = 0; j < i; j++) {
  352.         rc_wait4(pids[j], &sp, TRUE);
  353.         stats[j] = sp;
  354.         intr |= (sp == SIGINT);
  355.     }
  356.     setpipestatus(stats, i);
  357.     sigchk();
  358. }
  359.